import os
from BBS import settings
from django.db.models import Count, F
from django.shortcuts import render, HttpResponse, redirect, reverse
from django.http import JsonResponse
from app01 import models
from app01.myforms import MyRegForm
from PIL import Image, ImageDraw, ImageFont
import random
from io import BytesIO, StringIO
from django.contrib import auth
from django.contrib.auth.decorators import login_required
from django.db.models.functions import TruncMonth
import json
from django.db import transaction
from utils.mypage import Pagination
from bs4 import BeautifulSoup

"""
内存管理器模块
BytesIO: 临时帮你存储数据 返回的数据是二进制
StringIO: 临时帮你存储数据 返回的数据是字符串
"""


def get_random():
    return random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)

# Create your views here.


def register(request):
    form_obj = MyRegForm()
    if request.method == 'POST':
        form_obj = MyRegForm(request.POST)
        back_dic = {'code': 1000, 'msg': ''}
        if form_obj.is_valid():
            print(form_obj.cleaned_data)
            clean_data = form_obj.cleaned_data  # 将校验通过的数据字典复制给clean_data
            # 将字典里面的confirm_password删除
            clean_data.pop('confirm_password')
            file_obj = request.FILES.get('avatar')
            print('file_obj:', file_obj)
            """针对用户头像要判断是否传值 不能直接添加到字典中"""
            if file_obj:
                clean_data['avatar'] = file_obj
            models.UserInfo.objects.create_user(**clean_data)
            back_dic['url'] = reverse('login')
        else:
            back_dic['code'] = 2000
            back_dic['msg'] = form_obj.errors
        print(back_dic)
        return JsonResponse(back_dic)
    return render(request, 'register.html', locals())


def login(request):
    if request.method == 'POST':
        back_dic = {'code': 1000, 'msg': ''}
        username = request.POST.get('username')
        password = request.POST.get('password')
        code = request.POST.get('id_code')
        # 校验验证码是否正确
        if request.session.get('code').upper() == code.upper():
            # 校验用户名与密码
            user_obj = auth.authenticate(username=username, password=password)
            if user_obj:
                auth.login(request, user_obj)
                back_dic['url'] = reverse('home')
            else:
                back_dic['code'] = 2000
                back_dic['msg'] = '用户名或密码错误'
        else:
            back_dic['code'] = 3000
            back_dic['msg'] = '验证码错误'
        return JsonResponse(back_dic)
    return render(request, 'login.html')


def get_code(request):
    # 推到步骤1: 直接获取后端现成的二进制数据发送给前端
    # with open(r'static/img/3.jpg', 'rb') as f:
    #     data = f.read()
    # return HttpResponse(data)
    # 推到步骤2: 利用pillow模块产生图片, 文件存储繁琐IO操作效率低
    # img_obj = Image.new('RGB', (350, 35), get_random())
    # # 先将图片对象保存起来
    # with open('xxx.png', 'wb') as f:
    #     img_obj.save(f, 'png')
    #
    # # 在讲图片对象读取出来
    # with open('xxx.png', 'rb') as f:
    #     data = f.read()
    # return HttpResponse(data)

    # 推到步骤3:借助于内存管理器模块
    # img_obj = Image.new('RGB', (350, 35), get_random())
    # io_obj = BytesIO()  # 生成一个内存管理器对象  可以看成是文件句柄
    # img_obj.save(io_obj, 'png')
    # return HttpResponse(io_obj.getvalue())  # 从内存中读取二进制图片数据返回给前端

    # 推到步骤4: 写图片验证码
    img_obj = Image.new('RGB', (350, 35), get_random())
    img_draw = ImageDraw.Draw(img_obj)  # 画笔对象
    img_font = ImageFont.truetype('static/font/方正正中黑简体.ttf', size=30)  # 字体样式和大小

    # 随机验证码 5位数
    code = ''
    for i in range(5):
        random_upper = chr(random.randint(65, 90))
        random_lower = chr(random.randint(97, 122))
        random_int = str(random.randint(0, 9))
        tmp = random.choice([random_upper, random_lower, random_int])
        img_draw.text((5 + random.randint(4, 7) + 60 * i, random.randint(-1, 4)), tmp, get_random(), img_font)
        code += tmp
    print(code)
    request.session['code'] = code
    io_obj = BytesIO()
    img_obj.save(io_obj, 'png')
    return HttpResponse(io_obj.getvalue())


def home(request):
    article_queryset = models.Article.objects.all()
    return render(request, 'home.html', locals())


@login_required
def logout(request):
    auth.logout(request)
    return redirect('home')


@login_required
def set_password(request):
    if request.is_ajax():
        back_dic = {'code': 1000, 'msg': ''}
        if request.method == 'POST':
            old_password = request.POST.get('old_password')
            new_password = request.POST.get('new_password')
            confirm_password = request.POST.get('confirm_password')
            is_right = request.user.check_password(old_password)
            if is_right:
                if new_password == confirm_password:
                    request.user.set_password(new_password)
                    request.user.save()
                    back_dic['msg'] = '修改成功'
                else:
                    back_dic['code'] = 1001
                    back_dic['msg'] = '两次密码不一致'
            else:
                back_dic['code'] = 1002
                back_dic['msg'] = '原密码错误'
    return JsonResponse(back_dic)


def site(request, username, **kwargs):
    """

    :param request:
    :param username:
    :param kwargs: 如果该参数有值 也就是意味着需要对article-list进行筛选操作
    :return:
    """
    print(kwargs)
    user_obj = models.UserInfo.objects.filter(username=username).first()
    # 用户不存在返回404页面
    if not user_obj:
        return render(request, 'errors.html')
    blog = user_obj.blog
    article_list = models.Article.objects.filter(blog=blog)  # 侧边栏的筛选就是对article_list进一步筛选
    if kwargs:
        condition = kwargs.get('condition')
        param = kwargs.get('param')
        print(condition, param)
        if condition == 'category':
            article_list = article_list.filter(category_id=param)
            print(article_list)
        elif condition == 'tag':
            article_list = article_list.filter(tags__id=param)
            print(article_list)
        else:
            year, month = param.split('-')
            article_list = article_list.filter(create_time__year=year, create_time__month=month)
            print(article_list)

    # 1. 查询当前用户所有的分类及分类下的文章数

    return render(request, 'site.html', locals())


def article_detail(request, username, article_id):
    user_obj = models.UserInfo.objects.filter(username=username).first()
    blog = user_obj.blog
    article_obj = models.Article.objects.filter(pk=article_id, blog__userinfo__username=username).first()
    if not article_obj:
        return render(request, 'errors.html')
    comment_list = models.Comment.objects.filter(article=article_obj)
    return render(request, 'article_detail.html', locals())


def up_or_down(request):
    """
    1.校验用户是否登录
    2.自己不能给自己点赞点踩 判断当前文章是否是当前用户自己写的
    3.当前用户是否给当前文章点过了
    4.操作数据库了
    :param request:
    :return:
    """
    if request.is_ajax():
        back_dic = {'code': 1000, 'msg': ''}
        # 先判断当前用户是否登录
        if request.user.is_authenticated:
            article_id = request.POST.get('article_id')
            is_up = json.loads(request.POST.get('is_up'))  # 字符串
            print(is_up, type(is_up))  # true <class 'str'>
            # 2.判断当前文章是否是当前用户自己写的
            # 根据文章id查询文章对象 根据文章对象查询作者  跟request.user对比
            article_obj = models.Article.objects.filter(pk=article_id).first()
            if not article_obj.blog.userinfo == request.user:
                # 校验当前用户是否已经点了
                is_click = models.UpAndDown.objects.filter(user=request.user, article=article_obj)
                if not is_click:
                    # 操作数据库 记录数据  要同步操作普通字段
                    # 判断是点赞还是点踩
                    if is_up:
                        # 给点赞数+1
                        models.Article.objects.filter(pk=article_id).update(up_num=F('up_num') + 1)
                        back_dic['msg'] = '点赞成功'
                    else:
                        # 点踩数+1
                        models.Article.objects.filter(pk=article_id).update(down_num=F('down_num') + 1)
                        back_dic['msg'] = '点踩成功'
                    # 操作点赞点踩表
                    models.UpAndDown.objects.create(user=request.user, article=article_obj, is_up=is_up)
                else:
                    back_dic['code'] = 1001
                    back_dic['msg'] = '你已经点过了,不能再点了'
            else:
                back_dic['code'] = 1002
                back_dic['msg'] = '你个臭不要脸的!别给自己点赞'
        else:
            back_dic['code'] = 1003
            back_dic['msg'] = '请先<a href="/login/">登录</a>'
        return JsonResponse(back_dic)


def comment(request):
    # 自己也可以给自己的文章评论内容
    if request.is_ajax():
        if request.method == 'POST':
            back_dic = {'code': 1000, 'msg': ''}
            if request.user.is_authenticated:
                article_id = request.POST.get('article_id')
                content = request.POST.get('content')
                parent_id = request.POST.get('parent_id')
                # 直接操作评论表存储数据  两张表
                with transaction.atomic():
                    models.Article.objects.filter(pk=article_id).update(comment_num=F('comment_num') + 1)
                    models.Comment.objects.create(user=request.user, article_id=article_id, content=content, parent_id=parent_id)
                back_dic['msg'] = '评论成功'
            else:
                back_dic['code'] = 1001
                back_dic['msg'] = '用户未登录'
            return JsonResponse(back_dic)


@login_required
def backend(request):
    article_list = models.Article.objects.filter(blog=request.user.blog)
    page_obj = Pagination(current_page=request.GET.get('page', 1), all_count=article_list.count())
    page_queryset = article_list[page_obj.start: page_obj.end]
    return render(request, 'backend/backend.html', locals())


@login_required
def add_article(request):
    if request.method == 'POST':
        article_dic = {
            'title': request.POST.get('title'),
            'desc': request.POST.get('desc'),
            'content': request.POST.get('content'),
            'category_id': request.POST.get('category'),
        }
        tag_id_list = request.POST.get('tag')
        soup = BeautifulSoup(article_dic.get('content'), 'html.parser')
        # 获取所有的标签
        tags = soup.find_all()
        print(tags)
        for tag in tags:
            if tag.name == 'script':
                tag.decompose()
        article_dic.update(content=str(soup))
        article_obj = models.Article.objects.create(**article_dic, blog=request.user.blog)
        article_obj_list = [models.Article2Tag(article=article_obj, tag_id=i) for i in tag_id_list]
        # 批量插入
        models.Article2Tag.objects.bulk_create(article_obj_list)
        return redirect('backend')
    category_list = models.Category.objects.filter(blog=request.user.blog)
    tag_list = models.Tag.objects.filter(blog=request.user.blog)
    return render(request, 'backend/add_article.html', locals())


# 富文本上传图片
# 需要指定返回时的数据 {'error': 0, 'url': 'http://www....'}
def upload_image(request):
    back_dic = {'error': 0, }  # 先提前定义返回给编辑器的数据格式
    if request.method == 'POST':
        print(request.FILES)  # <MultiValueDict: {'imgFile': [<InMemoryUploadedFile: 3.jpg (image/jpeg)>]}>
        file_obj = request.FILES.get('imgFile')
        file_dir = os.path.join(settings.BASE_DIR, 'media', 'article_img')
        if not os.path.isdir(file_dir):
            os.mkdir(file_dir)
        file_path = os.path.join(file_dir, file_obj.name)
        with open(file_path, mode='wb') as f:
            for line in file_obj:
                f.write(line)
        back_dic['url'] = f'/media/article_img/{file_obj.name}'
    return JsonResponse(back_dic)


@login_required
def set_avatar(request):
    if request.method == 'POST':
        file_obj = request.FILES.get('avatar')
        # models.UserInfo.objects.filter(pk=request.user.pk).update(avatar=file_obj)  # 头像不会自动加avatar前缀
        user_obj = request.user
        user_obj.avatar = file_obj
        user_obj.save()
        return redirect('home')
    blog = request.user.blog
    username = request.user.username
    return render(request, 'set_avatar.html', locals())